package com.gpf.common;

import java.lang.reflect.Method;
import java.util.Date;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gpf.pojo.SysLog;
import com.gpf.server.SysLogServer;

import lombok.extern.slf4j.Slf4j;

/*
 * @Aspect//@Aspect 注解用于标识或者描述AOP中的切面类型，基于切面类型构建的对象用于为目标对象进行功能扩展或控制目标对象的执行。
 * 
 * @Slf4j
 * 
 * @Component public class SysLogAspect {
 * 
 * @Pointcut注解用于描述切面中的方法，并定义切面中的切入点（基于特定表达式的方式进行描述），在本案例中切入点表达式用的是bean表达式，
 * 这个表达式以bean开头，bean括号中的内容为一个spring管理的某个bean对象的名字。
 * 
 * @Pointcut("bean(sysUserServiceImpl)") public void logPointCut(){}
 * 
 * @Around注解用于描述切面中方法，这样的方法会被认为是一个环绕通知（核心业务方法执行之前和之后要执行的一个动作），
 * 
 * @Aournd注解内部value属性的值为一个切入点表达式或者是切入点表达式的一个引用(这个引用为一个@PointCut注解描述的方法的方法名)。
 * 
 * @Around("logPointCut()") public Object around(ProceedingJoinPoint jp) throws
 * Throwable{ //ProceedingJoinPoint类为一个连接点类型，此类型的对象用于封装要执行的目标方法相关的一些信息。只能用于@
 * Around注解描述的方法参数。 try { log.info("start:{}"+System.currentTimeMillis());
 * Object result=jp.proceed();//最终会调用目标方法
 * log.info("after:{}"+System.currentTimeMillis()); return result;//目标方法的执行结果
 * }catch(Throwable e) { log.error("after:{}",e.getMessage()); throw e; } } }
 */
@Aspect
@Component
public class SysLogAspect {
	
	private Logger log=LoggerFactory.getLogger(SysLogAspect.class);
	
	@Autowired
	private SysLogServer sysLogService;
	
	@Pointcut("@annotation(com.gpf.common.RequiredLog)")
	public void logPointCut(){}
	
	@Around("logPointCut()")
	public Object around(ProceedingJoinPoint jointPoint) throws Throwable{
		long startTime=System.currentTimeMillis();
		//执行目标方法(result为目标方法的执行结果)
		Object result=jointPoint.proceed();
		long endTime=System.currentTimeMillis();
		long totalTime=endTime-startTime;
		log.info("方法执行的总时长为:"+totalTime);
		saveSysLog(jointPoint,totalTime);
		return result;
	}
	private void saveSysLog(ProceedingJoinPoint point,long totleTime) throws NoSuchMethodException, SecurityException, JsonProcessingException{
		//1.获取日志信息
		MethodSignature ms= (MethodSignature)point.getSignature();
		Class<?> targetClass=point.getTarget().getClass();
		String className=targetClass.getName();
		//获取接口声明的方法
		String methodName=ms.getMethod().getName();
		Class<?>[] parameterTypes=ms.getMethod().getParameterTypes();
		//获取目标对象方法(AOP版本不同,可能获取方法对象方式也不同)
		Method targetMethod=targetClass.getDeclaredMethod(methodName,parameterTypes);
		//获取用户名,学完shiro再进行自定义实现,没有就先给固定值
		String username="admin";//ShiroUtils.getPrincipal().getUsername();
		//获取方法参数
		Object[] paramsObj=point.getArgs();
		System.out.println("paramsObj="+paramsObj);
		//将参数转换为字符串
		String params=new ObjectMapper().writeValueAsString(paramsObj);
		//2.封装日志信息
		SysLog log=new SysLog();
		log.setUsername(username);//登陆的用户
		//假如目标方法对象上有注解,我们获取注解定义的操作值
		RequiredLog requestLog=targetMethod.getDeclaredAnnotation(RequiredLog.class);
		if(requestLog!=null){
			log.setOperation(requestLog.value());
		}
		log.setMethod(className+"."+methodName);//className.methodName()
		log.setParams(params);//method params
		log.setIp(IPUtils.getIpAddr());//ip 地址
		log.setTime(totleTime);//0
		log.setCreatedTime(new Date());
		//3.保存日志信息
		sysLogService.saveObject(log);
	}
}

